package org.tabletopmachine.match.unit;

import de.matthiasmann.twl.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.geom.Line;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.Transform;
import org.newdawn.slick.geom.Vector2f;
import org.tabletopmachine.IDrawable;
import org.tabletopmachine.basics.Conversions;
import org.tabletopmachine.basics.Maths;
import org.tabletopmachine.board.GameBoard;
import org.tabletopmachine.exceptions.InvalidActionException;
import org.tabletopmachine.match.Player;

public class Unit implements IDrawable {
	private float _angle;

	private ModelBase _base;
	private final GameBoard _board;
	private Color _borderColor;
	private int _files;
	private Color _fillColor;
	private int _models;
	private String _name;
	private final Player _player;
	private Vector2f _pos;
	private TroopType _troopType;
	private boolean _champion, _standardbearer, _musician;
	private Weapon _weapon;

	public Unit() {
		_board = GameBoard.getBoard();
		setName("Unnamed");
		_troopType = TroopType.None;
		_base = ModelBase.None;
		_files = 1;
		_models = 1;
		_pos = new Vector2f(0.0f);
		_angle = 0.0f;
		_fillColor = Color.RED;
		_borderColor = Color.BLACK;
		_player = null;
		_champion = false;
		_standardbearer = false;
		_musician = false;
		setWeapon(Weapon.None);
	}

	public void setBase(ModelBase base) {
		_base = base;
	}

	public void setTroopType(TroopType troopType) {
		_troopType = troopType;
	}

	public void setChampion(boolean champion) {
		_champion = champion;
	}

	public void setStandardbearer(boolean standardbearer) {
		_standardbearer = standardbearer;
	}

	public void setMusician(boolean musician) {
		_musician = musician;
	}

	public String getName() {
		return _name;
	}

	public Player getPlayer() {
		return _player;
	}

	public int getRanks() {
		return computeRanks(_models, _files);
	}

	public static int computeRanks(int models, int files) {
		if ((float) models / files < 1)
			return 1;
		else {
			int fullRanks = models / files;
			if (models % files != 0)
				fullRanks++;
			return fullRanks;
		}
	}

	public TroopType getTroopType() {
		return _troopType;
	}

	private float getHeight() {
		return (float) (getRanks() * _base.getHeight() * 0.0393700787);
	}

	private float getWidth() {
		return (float) (_files * _base.getWidth() * 0.0393700787);
	}

	public float getAngle() {
		return _angle;
	}

	public void setAngle(float angle) {
		_angle = angle;
	}

	public Color getBorderColor() {
		return _borderColor;
	}

	public void setBorderColor(Color borderColor) {
		_borderColor = borderColor;
	}

	public int getFiles() {
		return _files;
	}

	public void setFiles(int files) {
		_files = files;
	}

	public Color getFillColor() {
		return _fillColor;
	}

	public void setFillColor(Color fillColor) {
		_fillColor = fillColor;
	}

	public int getModels() {
		return _models;
	}

	public void setModels(int models) {
		_models = models;
	}

	public ModelBase getBase() {
		return _base;
	}

	public GameBoard getBoard() {
		return _board;
	}

	public Vector2f getPos() {
		return _pos;
	}

	public boolean hasChampion() {
		return _champion;
	}

	public boolean hasStandardbearer() {
		return _standardbearer;
	}

	public boolean hasMusician() {
		return _musician;
	}

	public void setName(String name) {
		_name = name;
	}

	public void MoveBackward(float inches) throws InvalidActionException {
		if (inches <= 0)
			throw new InvalidActionException();
		Move(-inches);
	}

	public void MoveForward(float inches) throws InvalidActionException {
		if (inches <= 0)
			throw new InvalidActionException();
		Move(inches);
	}

	public void MoveLeft(float inches) throws InvalidActionException {
		if (inches <= 0)
			throw new InvalidActionException();
		Strafe(-inches);
	}

	public void MoveRight(float inches) throws InvalidActionException {
		if (inches <= 0)
			throw new InvalidActionException();
		Strafe(inches);
	}

	public void render(GameContainer gc, Graphics g) {
		org.newdawn.slick.Color PrevColor = g.getColor();

		Vector2f CurrentOnScreenPosition = _board.ConvertPositionToScreenCoordinates(_pos);
		float UnitWidth = _board.ConvertLengthToPixels(getWidth());
		float UnitHeight = _board.ConvertLengthToPixels(getHeight());
		Rectangle UnitBaseWithoutRotation = new Rectangle(CurrentOnScreenPosition.getX() - UnitWidth / 2, CurrentOnScreenPosition.getY(), UnitWidth, UnitHeight);
		Shape UnitBase = UnitBaseWithoutRotation.transform(Transform.createRotateTransform(_angle, CurrentOnScreenPosition.getX(),
				CurrentOnScreenPosition.getY()));

		g.setColor(Conversions.ConvertTwlToSlickColor(_fillColor));
		g.fill(UnitBase);
		g.setColor(Conversions.ConvertTwlToSlickColor(_borderColor));
		g.draw(UnitBase);

		g.setColor(PrevColor);
	}

	public void RotateAroundLeft(float inches) {
		Rotate(inches, 0);
	}

	public void RotateAroundRight(float inches) {
		Rotate(-inches, 1);
	}

	@Override
	public void update(GameContainer gc, int delta) {

	}

	private void Move(float inches) {
		float beta = (float) -(Math.PI / 2 - _angle);
		float MoveX = (float) (inches * (Math.sin(_angle) / Math.sin(Math.PI / 2)));
		float MoveY = (float) (inches * (Math.sin(beta) / Math.sin(Math.PI / 2)));

		_pos.x += MoveX;
		_pos.y += MoveY;
	}

	private void Rotate(float inches, int point) {
		Vector2f left = new Vector2f(_pos.x - getWidth() / 2, _pos.y);
		Vector2f right = new Vector2f(_pos.x + getWidth() / 2, _pos.y);
		left = _board.ConvertPositionToScreenCoordinates(left);
		right = _board.ConvertPositionToScreenCoordinates(right);
		Line line = new Line(left, right);

		Shape linerot = line.transform(Transform.createRotateTransform(_angle, line.getCenterX(), line.getCenterY()));

		float NewAngle = (_board.ConvertLengthToPixels(-inches) / (2 * line.length() * Maths.Pi)) * 2 * Maths.Pi;
		linerot = linerot.transform(Transform.createRotateTransform(NewAngle, linerot.getPoint(point)[0], linerot.getPoint(point)[1]));

		Line NewLine = new Line(_board.ConvertScreenCoordinatesToPosition(linerot.getPoint(0)[0], linerot.getPoint(0)[1]),
				_board.ConvertScreenCoordinatesToPosition(linerot.getPoint(1)[0], linerot.getPoint(1)[1]));
		_angle += NewAngle;
		_pos = new Vector2f(NewLine.getCenterX(), NewLine.getCenterY());
	}

	private void Strafe(float inches) {
		float alpha = (float) (Maths.Pi / 2 - _angle);

		float MoveX = (float) (inches * (Maths.sin(alpha) / Maths.sin(Maths.Pi / 2)));
		float MoveY = (float) (inches * (Maths.sin(_angle) / Maths.sin(Maths.Pi / 2)));

		_pos.x += MoveX;
		_pos.y += MoveY;
	}

	public String toString() {
		return _name;
	}

	public Weapon getWeapon() {
		return _weapon;
	}

	public void setWeapon(Weapon weapon) {
		_weapon = weapon;
	}
}